package com.wcs.exam.controller.callback.biz;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.wcs.exam.common.base.BaseBiz;
import com.wcs.exam.common.cache.CacheRedis;
import com.wcs.exam.common.constant.RedisConstant;
import com.wcs.exam.common.enums.PayTypeEnum;
import com.wcs.exam.common.enums.PayWayEnum;
import com.wcs.exam.common.server.pay.PayFace;
import com.wcs.exam.common.server.pay.req.TradeNotifyReq;
import com.wcs.exam.common.server.pay.resp.TradeNotifyResp;
import com.wcs.exam.common.server.pay.util.TradeStatusEnum;
import com.wcs.exam.common.server.pay.util.WxPayConfig;
import com.wcs.exam.common.server.pay.util.WxPayUtil;
import com.wcs.exam.controller.biz.OrderInfoCommonBiz;
import com.wcs.exam.controller.biz.SysConfigCommonBiz;
import com.wcs.exam.controller.biz.resp.SysConfigPayResp;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * CALLBACK-订单信息表
 *
 * @author wcs
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class CallbackOrderInfoBiz extends BaseBiz {

    @NotNull
    private final Map<String, PayFace> payFaceMap;

    @NotNull
    private final CacheRedis cacheRedis;

    @NotNull
    private final SysConfigCommonBiz sysConfigCommonBiz;
    @NotNull
    private final OrderInfoCommonBiz orderInfoCommonBiz;

    /**
     * 交易通知
     *
     * @param request        回调请求
     * @param payChannelCode 支付通道编码
     * @return 返回信息
     */
    public String payNotify(HttpServletRequest request, String payChannelCode) {
        // 获取支付通知参数
        TradeNotifyReq notifyParam = getTradeNotifyParam(request);

        // 获取支付类型
        PayTypeEnum payTypeEnum = PayTypeEnum.byPayImpl(payChannelCode);
        if (ObjectUtil.isNull(payTypeEnum)) {
            log.error("交易--回调通知--支付通道{}，对应支付方式不存在", payChannelCode);
            return "";
        }

        // 获取支付配置
        SysConfigPayResp configPay = sysConfigCommonBiz.getPay();
        if (ObjectUtil.isNull(configPay)) {
            log.error("交易--回调通知，支付配置不存在");
            return "";
        }

        // 校验并且获取支付配置
        if (PayWayEnum.ALI_PAY.equals(payTypeEnum.getPayWay())) {
            // 支付宝配置
            notifyParam.setAliPayConfig(configPay.getAliPayConfig());
        } else if (PayWayEnum.WX_PAY.equals(payTypeEnum.getPayWay())) {
            // 微信配置
            WxPayConfig wxPayConfig = configPay.getWxPayConfig();
            notifyParam.setWxPayConfig(wxPayConfig);

            // 获取微信证书
            String certCacheKey = RedisConstant.Other.WX_CERT + wxPayConfig.getWxPayMchId();
            String wxCert = cacheRedis.get(certCacheKey);
            if (StrUtil.isBlank(wxCert)) {
                // 获取微信平台证书；1：接口的频率限制: 单个商户号1000 次/s、2：定期调用该接口，间隔时间小于12 小时
                wxCert = WxPayUtil.downloadCert(wxPayConfig);
                if (StrUtil.isBlank(wxCert)) {
                    log.error("交易--回调通知,获取微信平台证书失败");
                    return "";
                }
                cacheRedis.set(certCacheKey, wxCert, 6, TimeUnit.HOURS);
            }
            notifyParam.setWxCert(wxCert);
        }

        // 获取支付通道
        PayFace payFace = payFaceMap.get(payChannelCode);
        if (ObjectUtil.isNull(payFace)) {
            log.error("交易--回调通知--交易通知获取支付通道：{}失败", payChannelCode);
            return "";
        }

        // 处理交易回调通知
        TradeNotifyResp resp = payFace.tradeNotify(notifyParam);
        Boolean res = false;
        if (resp.isSuccess() && TradeStatusEnum.SUCCESS.getCode().equals(resp.getTradeStatus())) {
            // 处理交易成功订单
            res = orderInfoCommonBiz.tradeSuccessHandle(Long.valueOf(resp.getTradeOrderNo()), resp.getTradeNo(), resp.getSuccessTime());
        }

        if (Boolean.FALSE.equals(res)) {
            log.error("交易--回调通知处理失败，{}", JSONUtil.toJsonStr(resp));
        }
        return resp.getReturnMsg();
    }

    /**
     * 获取交易通知参数
     *
     * @param request 通知请求
     * @return 交易通知参数
     */
    public TradeNotifyReq getTradeNotifyParam(HttpServletRequest request) {
        Map<String, String> headerMap = new HashMap<>();
        Map<String, String> queryParamMap = new HashMap<>();

        // 获取请求头
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();
            String val = request.getHeader(name);
            headerMap.put(name, val);
        }
        log.info("交易回调通知--header参数：{}", headerMap);

        // 获取Query参数
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String name = parameterNames.nextElement();
            String val = request.getParameter(name);
            queryParamMap.put(name, val);
        }
        log.info("交易回调通知--query参数：{}", queryParamMap);

        // 获取body参数
        String bodyParam = getBodyParam(request);
        log.info("交易回调通知--body参数：{}", bodyParam);

        return new TradeNotifyReq()
                .setHeaderMap(headerMap)
                .setQueryParamMap(queryParamMap)
                .setBodyParam(bodyParam);
    }

    /**
     * 获取HttpServletRequest请求body参数
     *
     * @param request 回调请求
     * @return body参数
     */
    private String getBodyParam(HttpServletRequest request) {
        BufferedReader br = null;
        try {
            StringBuilder sb = new StringBuilder();
            br = request.getReader();
            if (br != null) {
                String str;
                while ((str = br.readLine()) != null) {
                    sb.append(str);
                }
            }
            return sb.toString();
        } catch (IOException e) {
            log.error("交易回调通知--获取body参数失败", e);
            return null;
        } finally {
            if (null != br) {
                try {
                    br.close();
                } catch (IOException e) {
                    log.error("交易回调通知--获取body参数关闭流失败", e);
                }
            }
        }
    }
}
